package com.zlc.workflow.core.instance.cmd;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zlc.common.core.process.model.entity.StoneTaskHistory;
import com.zlc.common.core.process.model.vo.*;
import com.zlc.common.core.process.service.*;
import com.zlc.common.core.user.model.vo.MayanPlatUserVo;
import com.zlc.common.core.user.service.MayanPlatUserService;
import com.zlc.common.util.AssertUtils;
import com.zlc.common.util.wrapper.CollectionHelper;
import com.zlc.workflow.constant.NodeType;
import com.zlc.workflow.constant.TaskOperateTypeEnum;
import com.zlc.workflow.core.instance.model.QueryRejectNodeParam;
import com.zlc.workflow.core.instance.model.RejectNodeVo;
import com.zlc.workflow.core.instance.model.WorkflowDetail;
import com.zlc.workflow.flow.NodeModel;
import com.zlc.workflow.flow.NodeUtils;
import com.zlc.workflow.flow.node.TaskNode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.zlc.common.common.constant.WorkflowConstant.SYMBOL_QUOTATION_MARKS;

@Slf4j
public class QueryRejectNodeCmd extends AbstractCmd<List<RejectNodeVo>>{

    @Autowired
    private MayanPlatUserService mayanPlatUserService;
    @Autowired
    private StoneProcessService stoneProcessService;
    @Autowired
    private StoneProcessInstanceService stoneProcessInstanceService;
    @Autowired
    private StoneTaskService stoneTaskService;
    @Autowired
    private StoneTaskHistoryService stoneTaskHistoryService;
    @Autowired
    private StoneProcessNodeService stoneProcessNodeService;

    private final QueryRejectNodeParam workflowParam;

    private final String account;

    private final  String processId;

    private final MayanPlatUserVo nowUser;

    private final String processInstanceId;

    private final String taskId;

    private final StoneTaskVo taskVo;

    private final StoneProcessInstanceVo instanceVo;

    private TaskNode taskNode;

    private final  WorkflowDetail result = new WorkflowDetail();

    private StoneProcessVo processVo;

    /**
     * 获取当前命令对象的所有带 @Autowired注解的属性，然后从spring上下文中获取到注入进去
     *
     * @param params
     */
    public QueryRejectNodeCmd(Object params) {
        super(params);
        this.workflowParam = (QueryRejectNodeParam) params;
        this.processId = workflowParam.getProcessId();
        this.account = workflowParam.getAccount();
        this.processInstanceId = workflowParam.getProcessInstanceId();
        this.taskId = workflowParam.getTaskId();
        AssertUtils.isTrue(!ObjectUtils.isEmpty(processInstanceId),"流程实例ID缺失");
        AssertUtils.isTrue(!ObjectUtils.isEmpty(taskId),"流程任务ID缺失");
        result.setProcessInstanceId(processInstanceId);
        this.instanceVo = stoneProcessInstanceService.getByProcessInstanceId(processInstanceId);
        AssertUtils.isTrue(!ObjectUtils.isEmpty(instanceVo),"流程实例ID错误");
        this.taskVo = stoneTaskService.getByTaskId(taskId);
        AssertUtils.isTrue(!ObjectUtils.isEmpty(taskVo),"流程任务ID错误");

        AssertUtils.isTrue(!ObjectUtils.isEmpty(workflowParam.getAccount()),"操作人不可为空");
        this.nowUser = mayanPlatUserService.queryByUserAccount(workflowParam.getAccount());
        AssertUtils.isTrue(!ObjectUtils.isEmpty(this.nowUser),"操作人不存在");
        AssertUtils.isTrue(account.equals(taskVo.getActorId()),"无权操作");

        this.processVo = stoneProcessService.getByProcessId(processId);
        String content = NodeUtils.getProcessContent(processVo.getContent());
        this.taskNode = NodeUtils.getTaskNode(content, processId, taskVo.getNodeId());
    }

    @Override
    public List<RejectNodeVo> execute() {
        List<RejectNodeVo> nodeVos = new ArrayList<>();
        List<NodeModel> allPreTaskNode = getPreTaskNode(this.taskNode);
        //查询所有历史任务
        LambdaQueryWrapper<StoneTaskHistory> wrapper = stoneTaskHistoryService.wrapper()
                .eq(StoneTaskHistory::getProcessInstanceId, processInstanceId)
                .eq(StoneTaskHistory::getOperateType, TaskOperateTypeEnum.SUBMIT.getType())
                .eq(StoneTaskHistory::getNodeType, NodeType.TASK.value());
        List<StoneTaskHistoryVo> taskHistoryVos = stoneTaskHistoryService.queryVoList(wrapper);
        for(NodeModel nodeModel : allPreTaskNode){
            List<StoneTaskHistoryVo> historyVos = taskHistoryVos.stream()
                    .filter(t -> nodeModel.nodeId().equals(t.getNodeId()))
                    .sorted(Comparator.comparing(StoneTaskHistoryVo::getCreateTime)).collect(Collectors.toList());
            if(!ObjectUtils.isEmpty(historyVos)){
                RejectNodeVo nodeVo = new RejectNodeVo();
                nodeVo.setNodeId(nodeModel.nodeId());
                nodeVo.setNodeName(nodeModel.nodeName());
                nodeVo.setTime(historyVos.get(0).getExecuteTime());
                //操作人
                List<RejectNodeVo.RejectApproveUser> approveUsers = historyVos.stream().flatMap(h -> {
                    RejectNodeVo.RejectApproveUser approveUser = new RejectNodeVo.RejectApproveUser();
                    approveUser.setAccount(h.getActorId());
                    approveUser.setUsername(h.getActorName());
                    approveUser.setTaskId(h.getTaskId());
                    approveUser.setTime(h.getExecuteTime());
                    return Stream.of(approveUser);
                }).sorted(Comparator.comparing(RejectNodeVo.RejectApproveUser::getTime)).collect(Collectors.toList());
                List<RejectNodeVo.RejectApproveUser> distApproveUsers = CollectionHelper.distinct(approveUsers, RejectNodeVo.RejectApproveUser::getAccount);
                nodeVo.setUsers(distApproveUsers);
                nodeVos.add(nodeVo);
            }
        }
        nodeVos.sort(Comparator.comparing(RejectNodeVo::getTime));
        return nodeVos;
    }

    private List<NodeModel> getPreTaskNode(NodeModel nodeModel){
        //查询配置
        StoneProcessNodeVo processNodeVo = stoneProcessNodeService.getByNodeId(processId, nodeModel.getNodeId());
        List<NodeModel> allPreTaskNode = NodeUtils.getAllPreTaskNode(nodeModel);
        if(ObjectUtils.isEmpty(processNodeVo)){
            return allPreTaskNode;
        }
        Boolean rejectFlag = processNodeVo.getRejectFlag();
        if(!Boolean.TRUE.equals(rejectFlag)){
            return new ArrayList<>();
        }
        String canRejectNode = processNodeVo.getCanRejectNode();
        if(ObjectUtils.isEmpty(canRejectNode)){
            return allPreTaskNode;
        }
        List<String> nodeIds = StrUtil.split(canRejectNode, SYMBOL_QUOTATION_MARKS);
        return allPreTaskNode.stream().filter(n -> nodeIds.contains(n.getNodeId()))
                .collect(Collectors.toList());
    }
}
